﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace ProgNetComponents.Hosting
{
    /// <summary>
    /// HostObject
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class HostObject<T> : ICustomTypeDescriptor, INotifyPropertyChanged, IHostObject, IStateObject
    {
        /// <summary>
        /// Gets or sets the host.
        /// </summary>
        /// <value>
        /// The host.
        /// </value>
        public T Host
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        /// <value>
        /// The state.
        /// </value>
        public ObjectState State
        {
            get;
            set;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HostObject{T}" /> class.
        /// </summary>
        public HostObject()
        {
            Host = Activator.CreateInstance<T>();
            State = ObjectState.Added;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="HostObject{T}"/> class.
        /// </summary>
        /// <param name="host">The host.</param>
        public HostObject(T host)
        {
            Host = host;
            State = ObjectState.Unchanged;
        }
       
        #region ICustomTypeDescriptor Members
        /// <summary>
        /// noCustomTypeDesc
        /// </summary>
        private bool noCustomTypeDesc = true;
        /// <summary>
        /// Returns a collection of attributes for the specified component
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.AttributeCollection" /> containing the attributes for this object.
        /// </returns>
        AttributeCollection ICustomTypeDescriptor.GetAttributes()
        {
            return TypeDescriptor.GetAttributes(this, noCustomTypeDesc);
        }

        /// <summary>
        /// Gets class name using TypeDescriptor
        /// </summary>
        /// <returns>
        /// The class name of the object, or null if the class does not have a name.
        /// </returns>
        string ICustomTypeDescriptor.GetClassName()
        {
            return TypeDescriptor.GetClassName(this, noCustomTypeDesc);
        }
        /// <summary>
        /// Returns component name using TypeDescriptor
        /// </summary>
        /// <returns>
        /// The name of the object, or null if the object does not have a name.
        /// </returns>
        string ICustomTypeDescriptor.GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, noCustomTypeDesc);
        }
        /// <summary>
        /// Gets TypeConverter using TypeDescriptor
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.TypeConverter" /> that is the converter for this object, or null if there is no <see cref="T:System.ComponentModel.TypeConverter" /> for this object.
        /// </returns>
        TypeConverter ICustomTypeDescriptor.GetConverter()
        {
            return TypeDescriptor.GetConverter(this, noCustomTypeDesc);
        }
        /// <summary>
        /// Gets default event using TypeDescriptor
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptor" /> that represents the default event for this object, or null if this object does not have events.
        /// </returns>
        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
        {
            return TypeDescriptor.GetDefaultEvent(this, noCustomTypeDesc);
        }
        /// <summary>
        /// Gets default property using TypeDescriptor
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptor" /> that represents the default property for this object, or null if this object does not have properties.
        /// </returns>
        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
        {
            return TypeDescriptor.GetDefaultProperty(this, noCustomTypeDesc);
        }
        /// <summary>
        /// Gets editor using TypeDescriptor
        /// </summary>
        /// <param name="editorBaseType">A <see cref="T:System.Type" /> that represents the editor for this object.</param>
        /// <returns>
        /// An <see cref="T:System.Object" /> of the specified type that is the editor for this object, or null if the editor cannot be found.
        /// </returns>
        object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
        {
            return TypeDescriptor.GetEditor(this, editorBaseType, noCustomTypeDesc);
        }
        /// <summary>
        /// Gets Events using TypeDescriptor and specified attributes
        /// </summary>
        /// <param name="attributes">An array of type <see cref="T:System.Attribute" /> that is used as a filter.</param>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptorCollection" /> that represents the filtered events for this component instance.
        /// </returns>
        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
        {
            return TypeDescriptor.GetEvents(this, attributes, noCustomTypeDesc);
        }
        /// <summary>
        /// Gets Events using TypeDescriptor
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.ComponentModel.EventDescriptorCollection" /> that represents the events for this component instance.
        /// </returns>
        EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
        {
            return TypeDescriptor.GetEvents(this, noCustomTypeDesc);
        }
        /// <summary>
        /// Gets Properties using DynamicPropertyAttribute
        /// </summary>
        /// <param name="attributes">An array of type <see cref="T:System.Attribute" /> that is used as a filter.</param>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection" /> that represents the filtered properties for this component instance.
        /// </returns>
        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
        {
            return GetProperties();
        }
        /// <summary>
        /// Gets Properties using DynamicPropertyAttribute
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection" /> that represents the properties for this component instance.
        /// </returns>
        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
        {
            return GetProperties();
        }
        /// <summary>
        /// Retutns this object
        /// </summary>
        /// <param name="pd">A <see cref="T:System.ComponentModel.PropertyDescriptor" /> that represents the property whose owner is to be found.</param>
        /// <returns>
        /// An <see cref="T:System.Object" /> that represents the owner of the specified property.
        /// </returns>
        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }

        /// <summary>
        /// Returns the properties for this instance of a component.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection" /> that represents the properties for this component instance.
        /// </returns>
        private PropertyDescriptorCollection GetProperties()
        {
            PropertyDescriptorCollection p1 = TypeDescriptor.GetProperties(this, noCustomTypeDesc);
            PropertyDescriptorCollection p2 = TypeDescriptor.GetProperties(typeof(T));
            List<PropertyDescriptor> list = new List<PropertyDescriptor>();
            foreach (PropertyDescriptor p in p1)
            {
                list.Add(p);
            }
            foreach (PropertyDescriptor p in p2)
            {
                list.Add(new HostPropertyDescriptor(p));
            }
            return new PropertyDescriptorCollection(list.ToArray());
        }
        #endregion

        /// <summary>
        /// Called when [property changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="property">The property.</param>
        public void OnPropertyChanged(object sender, string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(sender, new PropertyChangedEventArgs(property));
            }
            if (State == ObjectState.Unchanged)
                State = ObjectState.Modified;
        }

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets the host object.
        /// </summary>
        /// <value>
        /// The host object.
        /// </value>
        object IHostObject.HostObject
        {
            get { return Host; }
        }

    }
}
